#include <tcl.h>

#include <id3/tag.h>
#include <id3/misc_support.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <io.h>

#include <cstdio>
#include <iostream>
#include <map>
#include <string>

#include "tcl_discotagger_types.h"
#include "tcl_discotagger.h"
#include "tcl_discotagger_support.h"

using namespace std;

int delete_tags(ID3_Tag* myTag)
{
	bool success = false;
	if (myTag)
	{
		myTag->Clear();
		myTag->Strip(ID3TT_ID3);
		myTag->Update();
	}
	return TCL_OK;
}

int tag_file(char* pFn, int del, int id3v1, int id3v2)
{
	ID3_Tag *pMyTag= new ID3_Tag();
	struct stat filestat;
	flags_t UpdFlags = ID3TT_NONE;
	if (id3v1) UpdFlags|=ID3TT_ID3V1;
	if (id3v2) UpdFlags|=ID3TT_ID3V2;
	if (stat(pFn, &filestat)) 
	{
		cout << "Couldn't stat file '" << pFn << endl;
		delete (pMyTag);
		return TCL_ERROR;
	}
	pMyTag->Link(pFn);
	if (del) delete_tags(pMyTag);
	// processing of frames here...
	for (TagCue::iterator tci = g_tagCue.begin(); tci!= g_tagCue.end(); tci++)
	{
		// frame to create
		ID3_Frame *myFrame = new ID3_Frame;
		// possible old frame
		ID3_Frame *pFrame;
		// if usertext frame find the matching one
		if (g_frames.find(tci->first)==g_frames.end()) {
			pFrame = pMyTag->FindTXXXFrame(tci->first);
			myFrame->SetID(ID3FID_USERTEXT);
			myFrame->Field(ID3FN_DESCRIPTION) = tci->first.c_str();
			myFrame->Field(ID3FN_TEXT) = tci->second.c_str();
		}
		else {
			pFrame = pMyTag->Find(g_frames[tci->first]);
			myFrame->SetID(g_frames[tci->first]);
			myFrame->Field(ID3FN_TEXT) = tci->second.c_str();
		}
		if (pFrame != NULL)
		{
			pMyTag->RemoveFrame(pFrame);
		}
		pMyTag->AttachFrame(myFrame);
	}
	pMyTag->Update(UpdFlags);
	g_tagCue.clear();
	delete (pMyTag);
	return TCL_OK;
}
int enque_tag(char* pKey,char* pValue)
{
	g_tagCue[pKey]=pValue;
#endif

	return TCL_OK;
}
int Discotagger(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) 
{
	if ((objc!=4)&&(objc!=6)) {
		printf("Usage:\n");
		printf(" discotagger set key value\n");
		printf(" discotagger tag filename delete id3v1 id3v2\n");
		return TCL_ERROR;
	};
	// parse arguments...
	char *pCommand;
	int CommandLength;
	pCommand = Tcl_GetStringFromObj(objv[1],&CommandLength);
	
	if(!strcmp(pCommand, "set") && objc == 4)
	{
		int keyLength,valueLength,retValue;
		char *pKey,*pValue;
		// utf8 hack
		// ******************************************************
		Tcl_Obj *arg2 = Tcl_DuplicateObj(objv[2]);
		Tcl_Obj *arg3 = Tcl_DuplicateObj(objv[3]);
		char *pKeyN,*pValueN;

		pKey = (char*)Tcl_GetByteArrayFromObj(arg2,&keyLength);
		pValue = (char*)Tcl_GetByteArrayFromObj(arg3,&valueLength);
		pKeyN=new char[keyLength+1];
		pKeyN[keyLength]='\0';
		pValueN=new char[valueLength+1];
		pValueN[valueLength]='\0';
		memcpy(pKeyN,pKey,keyLength);
		memcpy(pValueN,pValue,valueLength);
		retValue = enque_tag(pKeyN,pValueN);
		// clean up
		Tcl_DecrRefCount(arg2);
		Tcl_DecrRefCount(arg3);
		delete[](pKeyN);
		delete[](pValueN);
		// ******************************************************
		// pKey = Tcl_GetStringFromObj(objv[2],&keyLength);
		// pValue = Tcl_GetStringFromObj(objv[3],&valueLength);
		return retValue;
	}
	else if(!strcmp(pCommand, "tag") && objc == 6)
	{
		int del,id3v1,id3v2,ret,fnLength;
		char *fn;
		fn = Tcl_GetStringFromObj(objv[2],&fnLength);
		ret=Tcl_GetIntFromObj(interp,objv[3],&del);
		if ((ret==TCL_ERROR)||(del<0)||(del>1))
		{
			printf("illigal option\n");
			return TCL_ERROR;
		}
		ret=Tcl_GetIntFromObj(interp,objv[4],&id3v1);
		if ((ret==TCL_ERROR)||(id3v1<0)||(id3v1>1))
		{
			printf("illigal option\n");
			return TCL_ERROR;
		}
		ret=Tcl_GetIntFromObj(interp,objv[5],&id3v2);
		if ((ret==TCL_ERROR)||(id3v2<0)||(id3v2>1))
		{
			printf("illigal option\n");
			return TCL_ERROR;
		}
		return tag_file(fn,del,id3v1,id3v2);
	}
	else 
	{
		printf("Command not supported!\n");
		return TCL_ERROR;
	}

	return TCL_OK;
}
int Tcl_discotagger_Init(Tcl_Interp *interp)
{
	if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
		return TCL_ERROR;
	}
	Tcl_CreateObjCommand(interp, "discotagger", Discotagger, NULL, NULL);
	init_id3_mapping();
	return TCL_OK;
}